package main.java.com.fmanager.configurations;

import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

import main.java.com.fmanager.models.UserAdditionDetial;
import main.java.com.fmanager.utils.JwtTokenUtil;

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

	
	
    @Value("${security.signing-key}")
    private String signingKey;
    
    @Value("${security.security-name}")
    private String securityRealm;
    
    
    @Autowired
	private JwtAccessTokenConverter accessTokenConverter;
    
    @Autowired
    private UserDetailsService userDetailsService; 
    
    @Autowired
    private  TokenEnhancerChain tokenEnhancerChain;
    
    @Bean
    @Override
    protected AuthenticationManager authenticationManager() throws Exception {
       return super.authenticationManager();
    }
    
    
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
       auth.userDetailsService(userDetailsService)
               .passwordEncoder(new BCryptPasswordEncoder());
    }
    
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
       http
               .sessionManagement()
               .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
               .and()
               .httpBasic()
               .realmName(securityRealm)
               .and()
               .csrf()
               .disable().authorizeRequests().antMatchers("/oauth/token").permitAll(); //$NON-NLS-1$

    }
    
    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter() {
        	/**
        	 *   add addition information,  user_id  into token string
        	 */
			@Override
			public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
				Map<String, Object> info = new LinkedHashMap<String, Object>(accessToken.getAdditionalInformation());
				
				Object userAdditionDetial = authentication.getUserAuthentication().getPrincipal();
				if(userAdditionDetial instanceof UserAdditionDetial) {
					info.put(JwtTokenUtil.USER_ID, ((UserAdditionDetial) userAdditionDetial).getUserId());
				}
				if(accessToken instanceof DefaultOAuth2AccessToken) {
					((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(info);
				}
				return super.enhance(accessToken, authentication);
			}
        	
        };
        converter.setSigningKey(signingKey);
        return converter;
    }
    
    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(accessTokenConverter());
    }
    
    @Bean
    @Primary //Making this primary to avoid any accidental duplication with another token service instance of the same name
    public DefaultTokenServices tokenServices() {
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        defaultTokenServices.setTokenStore(tokenStore());
        defaultTokenServices.setSupportRefreshToken(true);
        defaultTokenServices.setTokenEnhancer(tokenEnhancerChain);
        return defaultTokenServices;
    }  
    
    
    @Bean 
    public TokenEnhancerChain tokenEnhancerChain() {
    	TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
    	enhancerChain.setTokenEnhancers(Arrays.asList(accessTokenConverter));
    	return enhancerChain;
    }
    
}
